from ctypes import *
import os
import ftplib
import time

is_debugging = False
notice_when_check_dir = True
keep_latest: bool = True
SUCCESS_DICT = {True: 'successfully', False: 'failed'}


class ftp_sync:
    ftp = ftplib.FTP()
    ftp.set_pasv(False)

    def __init__(self, host, port=21):
        self.ftp.connect(host, port)

    def login(self, user, passwd):
        self.ftp.login(user, passwd)
        print(self.ftp.welcome)

    # 下载指定目录下的指定文件，留空意味确保此前ftp和os的当前目录如预期
    def download_file(self, remote_file: str, local_file: str = ''):
        if not local_file:
            local_file = remote_file

        if keep_latest:
            if os.path.isfile(local_file):
                if ftp.ftp.voidcmd(f"MDTM {remote_file}")[4:] <= time.strftime('%Y%m%d%H%M%S', time.gmtime(os.path.getmtime(local_file))):
                    if is_debugging:
                        print(f'no download {remote_file}.')
                    return
        with open(local_file, 'wb') as f:
            print(f'downloading {remote_file} > {local_file} ......')
            self.ftp.retrbinary('RETR ' + remote_file, f.write)

    # 下载整个目录下的文件，请确保此前ftp和os的当前目录如预期
    def download_filetree(self, current_dir: str):
        if is_debugging:
            print("remote dir:", current_dir)
        if not os.path.exists(current_dir):
            os.makedirs(current_dir)

        self.ftp.cwd(current_dir)
        os.chdir(current_dir)

        remote_names = self.ftp.nlst()
        if is_debugging:
            print("remote names:", remote_names)
        for file in remote_names:
            # 要求所有文件都有扩展名！
            if not '.' in file:
                if is_debugging or notice_when_check_dir:
                    print(f'Checking directory: {file} ......')
                if not os.path.exists(file):
                    os.makedirs(file)
                self.download_filetree(file)
            else:
                self.download_file(file)
        self.ftp.cwd("..")
        os.chdir("..")
        return True

    # 从本地上传文件到ftp
    def upload_file(self, local_file: str, remote_file: str = ''):
        if not remote_file:
            remote_file = local_file

        bufsize = 1024

        if keep_latest:
            if remote_file in self.ftp.nlst():
                if self.ftp.voidcmd(f"MDTM {remote_file}")[4:] >= time.strftime('%Y%m%d%H%M%S', time.gmtime(os.path.getmtime(local_file))):
                    if is_debugging:
                        print(f'no upload {local_file}.')
                    return

        with open(local_file, 'rb') as f:
            print(f'uploading {local_file} > {remote_file} ......')
            self.ftp.storbinary('STOR ' + remote_file, f, bufsize)
            self.ftp.set_debuglevel(0)

    def mkdirs(self, paths: str):
        route = []
        while True:
            this_split = os.path.split(paths)
            route.append(this_split[1])
            if this_split[0]:
                paths = this_split[0]
            else:
                break
        route = route[::-1]

        for node in route:
            if node == '.':
                return
            if not node in self.ftp.nlst():
                self.ftp.mkd(node)
            self.ftp.cwd(node)

    # 上载整个目录下的文件，请确保此前ftp和os的当前目录如预期。

    def upload_filetree(self, current_dir: str):
        if is_debugging:
            print("remote dir:", current_dir)

        self.mkdirs(current_dir)
        os.chdir(current_dir)

        local_names = os.listdir()
        if is_debugging:
            print("remote names:", local_names)
        for file in local_names:
            if os.path.isdir(file):
                if is_debugging or notice_when_check_dir:
                    print(f'Checking directory: {file} ......')
                if not file in self.ftp.nlst():
                    self.ftp.mkd(file)
                self.upload_filetree(file)
            else:
                self.upload_file(file)
        self.ftp.cwd("..")
        os.chdir("..")
        return True

    def sync(self,dir:str):
        print('-----------------------')
        print('start downloading......')
        print(f'download {SUCCESS_DICT[self.download_filetree(dir)]}.')
        print('-----------------------')
        print('start uploading......')
        print(f'upload {SUCCESS_DICT[self.upload_filetree(dir)]}.')
        print('-----------------------')

    def bye(self):
        self.ftp.quit()


if __name__ == '__main__':
    ip: str = '172.19.208.1'
    port: int = 21
    username = 'anonymous'
    password = ''
    dir_to_sync = 'this'

    ftp = ftp_sync(ip, port)
    ftp.login(username, password)

    ftp.sync(dir_to_sync)
